package com.mai4j.maiadmin.service.impl;

import com.mai4j.maiadmin.misc.DbUtils;
import com.mai4j.maiadmin.model.dto.AdminMenuDTO;
import com.mai4j.maiadmin.model.dto.PageDTO;
import com.mai4j.maiadmin.model.http.datatables.Datatable;
import com.mai4j.maiadmin.model.http.datatables.Search;
import com.mai4j.maiadmin.service.AdminMenuDescriptionService;
import com.mai4j.maiadmin.service.AdminMenuService;
import com.mai4j.maiadmin.service.QueryService;
import com.mai4j.maiadmin.support.IdProvider;
import com.mai4j.maiadmin.support.JooqProvider;
import org.apache.commons.lang3.StringUtils;
import org.jooq.*;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import static com.mai4j.maiadmin.generated.Tables.ADMIN_MENU;
import static com.mai4j.maiadmin.generated.Tables.ADMIN_MENU_DESCRIPTION;

/**
 * 微信公众号请关注: 开源螺丝钉
 * <br>
 * 码云请关注: <a href="https://gitee.com/xiyoufang">https://gitee.com/xiyoufang</a>
 * <br>
 * 哔哩哔哩请关注: <a href="https://space.bilibili.com/438927834">https://space.bilibili.com/438927834</a>
 *
 * @author xiyoufang
 */
@Service
public class AdminMenuServiceImpl implements AdminMenuService {

    private final AdminMenuDescriptionService adminMenuDescriptionService;
    private final QueryService queryService;

    public AdminMenuServiceImpl(AdminMenuDescriptionService adminMenuDescriptionService, QueryService queryService) {
        this.adminMenuDescriptionService = adminMenuDescriptionService;
        this.queryService = queryService;
    }


    @Override
    public PageDTO<AdminMenuDTO> paginate(Datatable datatable) {
        DSLContext dsl = JooqProvider.dsl();
        SelectJoinStep<Record> query = dsl.select(ADMIN_MENU.asterisk()).from(ADMIN_MENU);
        String value = Optional.ofNullable(datatable.getSearch()).orElse(new Search()).getValue();
        List<Condition> conditions = new ArrayList<>();
        if (StringUtils.isNotBlank(value)) {
            value = "%" + value + "%";
            conditions.add(ADMIN_MENU.ADMIN_MENU_ID.in(
                    dsl.select(ADMIN_MENU_DESCRIPTION.ADMIN_MENU_ID)
                            .from(ADMIN_MENU_DESCRIPTION)
                            .where(ADMIN_MENU_DESCRIPTION.NAME.like(value))));
        }
        query.where(conditions);
        int count = queryService.fetchCount(query).count;
        List<AdminMenuDTO> adminMenuDTOS = count == 0 ? new ArrayList<>() : query
                .orderBy(ADMIN_MENU.ADMIN_MENU_ID.desc())
                .limit(datatable.getStart(), datatable.getLength())
                .fetch(recordAdminMenuDTORecordMapper());
        return new PageDTO<>(adminMenuDTOS, DbUtils.getPage(datatable), datatable.getLength(), DbUtils.getTotalPage(count, datatable), count);
    }

    @Cacheable(value = "data", key = "'adminmenu:' + #adminMenuId")
    @Override
    public AdminMenuDTO get(Long adminMenuId) {
        return JooqProvider.dsl().select(ADMIN_MENU.asterisk())
                .from(ADMIN_MENU)
                .where(ADMIN_MENU.ADMIN_MENU_ID.eq(adminMenuId))
                .fetchOne(recordAdminMenuDTORecordMapper());
    }

    @Transactional
    @CacheEvict(value = "data", key = "'adminmenu:' + #adminMenuDTO.getAdminMenuId()")
    @Override
    public AdminMenuDTO saveOrUpdate(AdminMenuDTO adminMenuDTO) {
        DSLContext dsl = JooqProvider.dsl();
        int count = dsl.fetchCount(dsl.selectFrom(ADMIN_MENU)
                .where(ADMIN_MENU.ADMIN_MENU_ID.eq(adminMenuDTO.getAdminMenuId())));
        long adminMenuId = count == 0 ? IdProvider.next() : adminMenuDTO.getAdminMenuId();
        if (count == 0) {
            dsl.insertInto(ADMIN_MENU)
                    .columns(ADMIN_MENU.ADMIN_MENU_ID,
                            ADMIN_MENU.CONTENT,
                            ADMIN_MENU.STATUS,
                            ADMIN_MENU.DATE_ADDED,
                            ADMIN_MENU.DATE_MODIFIED
                    )
                    .values(adminMenuId,
                            adminMenuDTO.getContent(),
                            adminMenuDTO.getStatus(),
                            LocalDateTime.now(),
                            LocalDateTime.now())
                    .execute();
        } else {
            dsl.update(ADMIN_MENU)
                    .set(ADMIN_MENU.CONTENT, adminMenuDTO.getContent())
                    .set(ADMIN_MENU.STATUS, adminMenuDTO.getStatus())
                    .set(ADMIN_MENU.DATE_MODIFIED, LocalDateTime.now())
                    .where(ADMIN_MENU.ADMIN_MENU_ID.eq(adminMenuId))
                    .execute();
        }
        adminMenuDescriptionService.saveDescriptions(adminMenuId, adminMenuDTO.getDescriptions());
        return get(adminMenuId);
    }

    private RecordMapper<Record, AdminMenuDTO> recordAdminMenuDTORecordMapper() {
        return record -> {
            AdminMenuDTO adminMenuDTO = record.into(AdminMenuDTO.class);
            adminMenuDTO.setDescriptions(adminMenuDescriptionService.get(adminMenuDTO.getAdminMenuId()));
            return adminMenuDTO;
        };
    }
}
